import { useCallback, useEffect, useRef, useState } from "react";
import useLatestRef from "./useLatestRef";

type UseFetchStateOptions<T, U> = {
  fetchFn: (data: T) => Promise<U>;
  immediate?: boolean;
};

type FetchState<T> = {
  isPending: boolean;
  isSuccess: boolean;
  data: T | null;
  isError: boolean;
  error: any;
};

export default function useFetchState<T = unknown, U = unknown>(
  fetchFn: (data: T, signal: AbortSignal) => Promise<U>,
) {
  const fetchFnRef = useLatestRef(fetchFn);
  const [state, setState] = useState<FetchState<U>>({
    isPending: false,
    isSuccess: false,
    data: null,
    isError: false,
    error: null,
  });
  const abortControllerRef = useRef<AbortController>();

  const fetcher = useCallback(
    async (data: T) => {
      const fetchFn = fetchFnRef.current;
      const abortController = new AbortController();
      abortControllerRef.current = abortController;
      try {
        setState({
          isPending: true,
          isSuccess: false,
          data: null,
          isError: false,
          error: null,
        });
        const res = await fetchFn(data, abortController.signal);
        setState({
          isPending: false,
          isSuccess: true,
          data: res,
          isError: false,
          error: null,
        });
      } catch (error) {
        console.error("fetch error", error);
        setState({
          isPending: false,
          isSuccess: false,
          data: null,
          isError: true,
          error,
        });
      }
    },
    [fetchFnRef],
  );

  const cancel = useCallback((reason?: any) => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort(reason);
      abortControllerRef.current = undefined;
    }
  }, []);

  return { ...state, refetch: fetcher, cancel };
}
